C + + do {... The meaning and usage of while of 0

  • 2020-04-02 02:58:20
  • OfStack

In the Linux kernel and other open source code, you will often encounter code like this:

Do {
  .
} while (0)
This code is not a loop, do.. While doesn't seem to make any sense here, so why use it?

Actually, do{... }while(0) does more than beautify your code. Looked up some data, summed up to write like this mainly has the following advantages:

1. Assist in defining complex macros to avoid errors in reference:

For example, suppose you need to define a macro that:


#define DOSOMETHING()
               foo1();
               foo2();

The purpose of this macro is that when DOSOMETHING() is called, the functions foo1() and foo2() are both called. But if you call it this way:


if(a>0)
    DOSOMETHING();

Because macros are expanded during preprocessing, you're actually writing code that looks like this:


if(a>0)
    foo1();
foo2();

This is problematic because foo2() will be executed regardless of whether a is greater than 0, causing an error in the program.

So just wrap foo1() and foo2() using {}?

We are used to adding a semicolon to the right side of the statement when we are writing code. If we use {} in a macro, the code is like this: "{... };" And this is what it looks like:


if(a>0)
{
    foo1();
    foo2();
};

It won't even compile. So, many talents have adopted do{... } while (0);


#define DOSOMETHING()
        do{
          foo1();
          foo2();
        }while(0)
...
 
if(a>0)
    DOSOMETHING();
...

In this way, the macros are expanded so that the original semantics are retained. GCC provides declarative Expressions instead of do {... } while (0);

So you can also define macros like this:


#define DOSOMETHING() ({
        foo1();
        foo2();
})

2. Avoid using goto to control program flow uniformly:

In some functions, we often do some final work before the function return, such as free to drop a block of memory to start the function malloc, goto has always been a relatively simple method:


int foo()
{
    somestruct* ptr = malloc(...);
 
    dosomething...;
    if(error)
    {
        goto END;
    }
 
    dosomething...;
    if(error)
    {
        goto END;
    }
    dosomething...;
 
END:
    free(ptr);
    return 0;
 
}

Since goto does not conform to the structure of software engineering and may make the code difficult to understand, many people do not advocate the use of it, so this time can use do{}while(0) for unified management:


int foo()
{
    somestruct* ptr = malloc(...);
 
    do{
        dosomething...;
        if(error)
        {
            break;
        }
 
        dosomething...;
        if(error)
        {
            break;
        }
        dosomething...;
    }while(0);
 
    free(ptr);
    return 0;
 
}

Instead of goto, the main body of the function is contained with do()while(0), and the break is used to replace goto, and the subsequent processing is done after the while, to achieve the same effect.

3. Avoid warnings caused by empty macros

Because of the limitation of different architectures in the kernel, empty macros are often used. When compiling, an empty macro will give a warning. To avoid such a warning, you can use do{}while(0) to define an empty macro:

# define EMPTYMICRO do {} while (0)
4. Define a separate function block to implement complex operations:

Use do{}while(0) when you have complex functions and many variables and you don't want to add a function. , where you can define variables without having to worry about their names repeating before or after functions.


Related articles: